﻿using System;
using System.Reflection;
using BitButterECS.Event;
using BitButterECS.Event.EventArgs;
using BitButterECS.Utils;

namespace BitButterECS.System
{
    public abstract class TriggerSystem : BaseSystem
	{
        public TriggerSystem() : base()
        {
        }

        protected override void RegisterEventHandlers()
        {
            base.RegisterEventHandlers();

            foreach (Type t in UpdateOnEventAttribute.GetListeningEvents(this))
            {
                var evt = FindEventHandlerInHierarchy(t);
                if (evt != null)
                {
                    var adder = evt.GetAddMethod();
                    if (adder != null)
                    {
                        var handler = GetType().GetMethod(nameof(OnUpdateEvent), BindingFlags.Instance | BindingFlags.NonPublic);
                        adder.Invoke(null, new object[] { Delegate.CreateDelegate(evt.EventHandlerType, this, handler) });
                    }
                }
            }
        }

        protected override void UnregisterEventHandlers()
        {
            base.UnregisterEventHandlers();

            foreach (Type t in UpdateOnEventAttribute.GetListeningEvents(this))
            {
                var evt = FindEventHandlerInHierarchy(t);
                if (evt != null)
                {
                    var remover = evt.GetRemoveMethod();
                    if (remover != null)
                    {
                        var handler = GetType().GetMethod(nameof(OnUpdateEvent), BindingFlags.Instance | BindingFlags.NonPublic);
                        remover.Invoke(null, new object[] { Delegate.CreateDelegate(evt.EventHandlerType, this, handler) });
                    }
                }
            }
        }

        void OnUpdateEvent(object sender, BaseEventArgs e)
        {
            if (IsEnabled)
            {
                UpdateOnEvent(sender, e);
            }
        }

        protected abstract void UpdateOnEvent(object sender, BaseEventArgs e);

        EventInfo FindEventHandlerInHierarchy(Type eventType)
        {
            var currentType = eventType;
            while (ECSUtils.IsSubclassOfRawGeneric(typeof(BaseEvent<>), currentType))
            {
                var evt = currentType.GetEvent("Handler", BindingFlags.Static | BindingFlags.Public);
                if (evt != null)
                {
                    return evt;
                }
                currentType = currentType.BaseType;
            }
            return null;
        }
    }
}
